﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JESAI.HtmlTemplate.Pdf.Models;
using JESAI.HtmlTemplate.Pdf.Utils;
using JESAI.HtmlTemplate.Pdf.HtmlPaster;
using System.IO;
using JESAI.HtmlTemplate.Pdf.MemoryCache;

#if NET461 || NET45
using System.Drawing.Imaging;
using TuesPechkin;
#else
using Microsoft.AspNetCore.Mvc.ViewEngines;
using DinkToPdf;
#endif

namespace JESAI.HtmlTemplate.Pdf
{
    public class PdfByHtmlTemplateExporter : IExportPdfByHtmlTemplate
    {
        private readonly IHtmlByRazorTemplateExporter _htmlByRazorTemplateExporter;
        private readonly ICacheService _cache;
        public PdfByHtmlTemplateExporter(IHtmlByRazorTemplateExporter htmlByRazorTemplateExporter, ICacheService cache)
        {
            _htmlByRazorTemplateExporter = this._htmlByRazorTemplateExporter;
            _cache = cache;
        }
        public PdfByHtmlTemplateExporter()
        {
            _htmlByRazorTemplateExporter = new HtmlByRazorTemplateExporter();
            _cache = new MemoryCacheService();
        }
#if NET461 
        private static readonly IConverter PdfConverter = new ThreadSafeConverter(new PdfToolset(
            new WinAnyCPUEmbeddedDeployment(
                new TempFolderDeployment())));

#elif NET45
  private static readonly IConverter PdfConverter = new ThreadSafeConverter(new RemotingToolset<PdfToolset>(
            new Win32EmbeddedDeployment(
                new TempFolderDeployment())));
#else
        private static readonly SynchronizedConverter PdfConverter = new SynchronizedConverter(new PdfTools());

#endif
        public async Task<byte[]> ExportByHtmlTemplateAsync<T>(T data, string razorTemplate) where T : class
        {
            var exporterAttribute = GetAttribute<T>();
           var htmlString=await  _htmlByRazorTemplateExporter.ExportHtmlByRazorTemplateAsync<T>(data, razorTemplate);
#if NET461 || NET45
            var result = ExportPdfNet(htmlString, exporterAttribute);
            return result;
#else
            var result = ExportPdf(htmlString, exporterAttribute);
            return await Task.FromResult(result);
#endif


        }
#if NET461 || NET45
        private byte[] ExportPdfNet(string htmlString,
           PdfExportAttribute pdfExporterAttribute)
        {
            var objSetting = new ObjectSettings
            {
                HtmlText = htmlString,
                CountPages = pdfExporterAttribute.IsEnablePagesCount ? true : (bool?)null,
                WebSettings = { DefaultEncoding = Encoding.UTF8.BodyName },
                //HeaderSettings = pdfExporterAttribute?.HeaderSettings,
                //FooterSettings = pdfExporterAttribute?.FooterSettings
            };
            if (pdfExporterAttribute?.HeaderSettings != null)
                objSetting.HeaderSettings = pdfExporterAttribute?.HeaderSettings;
            if (pdfExporterAttribute?.FooterSettings != null)
                objSetting.FooterSettings = pdfExporterAttribute?.FooterSettings;
            var htmlToPdfDocument = new HtmlToPdfDocument
            {
                GlobalSettings =
                {
                    PaperSize =pdfExporterAttribute?.PaperWidth>0&&pdfExporterAttribute?.PaperHeight>0?pdfExporterAttribute?.PaperKind:new PechkinPaperSize(pdfExporterAttribute?.PaperWidth.ToString(),pdfExporterAttribute?.PaperHeight.ToString()),
                    Orientation = pdfExporterAttribute?.Orientation, ProduceOutline = true,
                    DocumentTitle = pdfExporterAttribute?.Name
                },
                Objects =
                {
                    objSetting
                }
            };
            var result = PdfConverter.Convert(htmlToPdfDocument);

            return result;
        }
#else
        private byte[] ExportPdf(string htmlString, PdfExportAttribute pdfExportAttribute )
        {
            var objSetting = new ObjectSettings
            {
                HtmlContent = htmlString,
                PagesCount = pdfExportAttribute.IsEnablePagesCount ? true : (bool?)null,
                WebSettings = { DefaultEncoding = Encoding.UTF8.BodyName },
                HeaderSettings= pdfExportAttribute?.HeaderSettings,
                FooterSettings= pdfExportAttribute?.FooterSettings,

            };

            var htmlToPdfDocument = new HtmlToPdfDocument
            {
                GlobalSettings =
                {
                   
                    PaperSize =pdfExportAttribute?.PaperWidth==0&&pdfExportAttribute?.PaperHeight==0?pdfExportAttribute?.PaperKind:new PechkinPaperSize(pdfExportAttribute?.PaperWidth.ToString(),pdfExportAttribute?.PaperHeight.ToString()),
                    Orientation = pdfExportAttribute?.Orientation,
                    ColorMode = ColorMode.Color,
                    DocumentTitle = pdfExportAttribute?.Name
                },
                Objects =
                {
                    objSetting
                }
            };

            var result = PdfConverter.Convert(htmlToPdfDocument);
            return result;
        }
#endif

        private static PdfExportAttribute GetAttribute<T>() where T : class
        {
            var type = typeof(T);
            var exporterTableAttribute = type.GetAttribute<PdfExportAttribute>(true);
            if (exporterTableAttribute != null)
                return exporterTableAttribute;

            var export =  new PdfExportAttribute();
            return export;
        }

        public async Task<byte[]> ExportByHtmlAsync<T>(string html) where T : class
        {
            var exporterAttribute = GetAttribute<T>();

#if NET461 || NET45
            var result = ExportPdfNet(html, exporterAttribute);
            return result;//await Task.FromResult(result);
#else
            var result =  ExportPdf(html, exporterAttribute);
            return await Task.FromResult(result);
#endif


        }

        public async Task<byte[]> ExportByHtmlTemplatePersistAsync<T>(T data, string razorTemplate) where T : class
        {        
            var exporterAttribute = GetAttribute<T>();

            var htmlString = await _htmlByRazorTemplateExporter.ExportHtmlByRazorTemplateAsync<T>(data, razorTemplate);
            byte[] result = await ExportPdfPersistAsync(data, exporterAttribute, htmlString);
#if NET461 || NET45
            return result;
#else
            return await Task.FromResult(result);
#endif
        }

        private async Task<byte[]> ExportPdfPersistAsync<T>(T data, PdfExportAttribute exporterAttribute, string htmlString) where T : class
        {
          
            var result = new byte[] { };
            if (exporterAttribute.IsEnableSaveFile)
            {
                //var fileRootPath = HttpContext.Current.Server.MapPath("~/" + exporterAttribute.SaveFileRootPath);
                if (!Directory.Exists(exporterAttribute.SaveFileRootPath))
                    Directory.CreateDirectory(exporterAttribute.SaveFileRootPath);

                var hashCode = MD5HashUtil.GetMD5HashString(data);
                var filePath = exporterAttribute.SaveFileRootPath+"\\" + hashCode + ".pdf";
                if (File.Exists(filePath))
#if NET461 || NET45
                    result = File.ReadAllBytes(filePath);
#else
                    result = await File.ReadAllBytesAsync(filePath);
#endif
                else
                {

#if NET461 || NET45
                    result = ExportPdfNet(htmlString, exporterAttribute);
                    File.WriteAllBytes(filePath, result);
#else
                    result =  ExportPdf(htmlString, exporterAttribute);
                    await File.WriteAllBytesAsync(filePath, result);
#endif
                }

            }
            if (exporterAttribute.IsEnableCache)
            {
                var hashCode = MD5HashUtil.GetMD5HashString(data);
                if(_cache.Exists(hashCode))
                result=_cache.Get<byte[]>(hashCode);
                else
                {
#if NET461 || NET45
                    result = ExportPdfNet(htmlString, exporterAttribute);
#else
                    result = ExportPdf(htmlString, exporterAttribute);
#endif
                    _cache.Add<byte[]>(hashCode, result, exporterAttribute.CacheTimeSpan,true);
                }
            }
            else
            {
#if NET461 || NET45
                 result = ExportPdfNet(htmlString, exporterAttribute);
#else
                result = ExportPdf(htmlString, exporterAttribute);
#endif
            }

            return result;
        }

        public async Task<byte[]> ExportByHtmlPersistAsync<T>(T data, string html) where T : class
        {
            var exporterAttribute = GetAttribute<T>();
            byte[] result = await ExportPdfPersistAsync(data, exporterAttribute, html);
#if NET461 || NET45
            return result;//await Task.FromResult(result);
#else
            return await Task.FromResult(result);
#endif
        }

        public byte[] ExportByHtmlTemplate<T>(T data, string razorTemplate) where T : class
        {
            var exporterAttribute = GetAttribute<T>();
            var htmlString =  _htmlByRazorTemplateExporter.ExportHtmlByRazorTemplate<T>(data, razorTemplate);
#if NET461 || NET45
            var result = ExportPdfNet(htmlString, exporterAttribute);
            return result;
#else
            var result = ExportPdf(htmlString, exporterAttribute);
            return result;
#endif
        }

        public byte[] ExportByHtmlTemplatePersist<T>(T data, string razorTemplate) where T : class
        {
            var exporterAttribute = GetAttribute<T>();

            var htmlString =  _htmlByRazorTemplateExporter.ExportHtmlByRazorTemplate<T>(data, razorTemplate);
            byte[] result =  ExportPdfPersist(data, exporterAttribute, htmlString);
            return result;
        }

        public byte[] ExportByHtmlPersist<T>(T data, string html) where T : class
        {
            var exporterAttribute = GetAttribute<T>();
            byte[] result =  ExportPdfPersist(data, exporterAttribute, html);
            return result;
        }

        public byte[] ExportByHtml<T>(string html) where T : class
        {
            var exporterAttribute = GetAttribute<T>();

#if NET461 || NET45
            var result = ExportPdfNet(html, exporterAttribute);
            return result;//await Task.FromResult(result);
#else
            var result =  ExportPdf(html, exporterAttribute);
            return result;
#endif
        }

        private byte[] ExportPdfPersist<T>(T data, PdfExportAttribute exporterAttribute, string htmlString) where T : class
        {

            var result = new byte[] { };
            if (exporterAttribute.IsEnableSaveFile)
            {
                if (!Directory.Exists(exporterAttribute.SaveFileRootPath))
                    Directory.CreateDirectory(exporterAttribute.SaveFileRootPath);

                var hashCode = MD5HashUtil.GetMD5HashString(data);
                var filePath = exporterAttribute.SaveFileRootPath + "\\" + hashCode + ".pdf";
                if (File.Exists(filePath))
                    result = File.ReadAllBytes(filePath);
                else
                {
#if NET461 || NET45
                    result = ExportPdfNet(htmlString, exporterAttribute);
#else
                    result = ExportPdf(htmlString, exporterAttribute);
#endif
                    File.WriteAllBytes(filePath, result);

                }

            }
            if (exporterAttribute.IsEnableCache)
            {
                var hashCode = MD5HashUtil.GetMD5HashString(data);
                if (_cache.Exists(hashCode))
                    result = _cache.Get<byte[]>(hashCode);
                else
                {
#if NET461 || NET45
                    result = ExportPdfNet(htmlString, exporterAttribute);
#else
                    result = ExportPdf(htmlString, exporterAttribute);
#endif
                    _cache.Add<byte[]>(hashCode, result, exporterAttribute.CacheTimeSpan, true);
                }
            }
            else
            {
#if NET461 || NET45
                result = ExportPdfNet(htmlString, exporterAttribute);
#else
                result = ExportPdf(htmlString, exporterAttribute);
#endif
            }

            return result;
        }
    }
}
